package com.easepal.socketiolib;

import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;

import com.easepal.socketiolib.callback.ActionListener;
import com.easepal.socketiolib.callback.EventsListener;
import com.easepal.socketiolib.log.LogUtil;
import com.easepal.socketiolib.model.BaseModel;
import com.easepal.socketiolib.model.SocketModel;
import com.easepal.socketiolib.rxbus.RxBus;
import com.easepal.socketiolib.rxbus.event.ConnectActionEvent;
import com.easepal.socketiolib.rxbus.event.ConnectStatusEvent;
import com.easepal.socketiolib.rxbus.event.MessageEvent;
import com.easepal.websocketlib.BuildConfig;
import com.google.gson.Gson;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.functions.Consumer;

/**
 * 作者: bright.lin@medisana.net.cn
 * 时间: 2018/12/6
 * 标题: WebSocket客户端
 * 描述:
 * 修改: 由 bright.lin@medisana.net.cn 修改于 2018/12/6
 */
@SuppressWarnings("unused")
public class WebSocketClient {
    private static final String TAG = WebSocketClient.class.getName();

    // WebSocketClient对象
    private volatile static WebSocketClient webSocketClient = null;
    // Application上下文
    private Application mContext;

    /**
     * 处理后台服务绑定
     */
    private final class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            webSocketService = ((WebSocketServiceBinder) binder).getService();
            bindedService = true;
            doConnect();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            webSocketService = null;
        }
    }

    // 后台服务绑定回调接口
    private final MyServiceConnection mServiceConnection = new MyServiceConnection();
    // 线程池
    private static final ExecutorService pool = Executors.newCachedThreadPool();
    // WebSocket服务
    private WebSocketService webSocketService;
    // 服务已绑定标志位
    private volatile boolean bindedService = false;

    // Socket连接对象
    private SocketConnection socketConnection;
    // 服务器基址
    private String baseUrl = null;
    // 服务器地址
    private String serverUrl = null;

    // Disposable容器-RxJava
    private CompositeDisposable compositeDisposable;
    // 是否注册RxBus事件总线
    private volatile boolean registerRxBus;

    // 异步标签数组
    private final SparseArray<ActionListener> tokenMap = new SparseArray<>();
    // 异步标签数量
    private int tokenNumber = 0;
    // 连接异步标签
    private String connectToken = null;
    // 事件回调列表
    private ArrayList<EventsListener> eventsListeners = new ArrayList<>();

    /**
     * 带参构造器
     *
     * @param application Application上下文
     */
    private WebSocketClient(Application application) {
        mContext = application;
        compositeDisposable = new CompositeDisposable();
        LogUtil.init("SIOLIB-", true);
    }

    /**
     * 获取WebSocketClient单例
     *
     * @param application Application上下文
     * @return WebSocketClient单例
     */
    public static WebSocketClient getInstance(Application application) {
        if (null == webSocketClient) {
            synchronized (WebSocketClient.class) {
                if (null == webSocketClient) {
                    webSocketClient = new WebSocketClient(application);
                }
            }
        }
        return webSocketClient;
    }

    /**
     * 连接
     *
     * @param serverToken    服务器token
     * @param actionListener 动作监听回调
     */
    public void connect(String serverToken, ActionListener actionListener) {
        this.serverUrl = getBaseUrl() + "?token=" + serverToken;
        LogUtil.e("SockeStatus_connect", "serverUrl:" + serverUrl);
        connectToken = storeListener(actionListener);

        if (null == webSocketService) {
            Intent serviceStartIntent = new Intent();
            serviceStartIntent.setClassName(mContext, WebSocketService.class.getName());
            Object service = mContext.startService(serviceStartIntent);
            if (null == service && null != actionListener) {
                actionListener.onFailure(ErrorInfo.ERR_SERVICE_NULL);
            }

            mContext.bindService(serviceStartIntent, mServiceConnection, Context.BIND_AUTO_CREATE);

            if (!registerRxBus) registerRxBus();
        } else {
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    doConnect();
                    if (!registerRxBus) registerRxBus();
                }
            });
        }
    }

    /**
     * 获取连接状态
     *
     * @return 连接状态
     */
    public boolean isConnected() {
        if (null != webSocketService) {
            return webSocketService.isConnected();
        } else {
            return false;
        }
    }

    /**
     * 发送消息
     *
     * @param text           消息
     * @param actionListener 动作监听回调
     */
    public void sent(String text, ActionListener actionListener) {
        try {
            webSocketService.sent(text);
            if (null != actionListener) {
                actionListener.onSuccess();
            }
        } catch (Exception e) {
            if (null != actionListener) {
                actionListener.onFailure(e.getMessage());
            }
        }
    }

    /**
     * 断开连接
     */
    public void disconnect() {
        if (null != webSocketService) {
            webSocketService.close();
            webSocketClient = null;
        }
    }

    /**
     * 注册事件监听回调
     *
     * @param eventsListener 事件监听回调
     */
    public void registerListener(EventsListener eventsListener) {
        if (!eventsListeners.contains(eventsListener)) {
            eventsListeners.add(eventsListener);
        }
    }

    /**
     * 注销事件监听回调
     *
     * @param eventsListener 事件监听回调
     */
    public void unregisterListener(EventsListener eventsListener) {
        eventsListeners.remove(eventsListener);
    }

    /**
     * 设置服务器基址
     * @param baseUrl 服务器基址
     */
    public void setBaseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    /**
     * 释放资源
     */
    public void onDestroy() {
        eventsListeners.clear();

        if (registerRxBus) {
            synchronized (WebSocketClient.this) {
                compositeDisposable.clear();
                registerRxBus = false;
            }
        }

        if (null != mContext && bindedService) {
            try {
                mContext.unbindService(mServiceConnection);
                bindedService = false;
            } catch (IllegalArgumentException e) {
                LogUtil.d(TAG, e.getMessage());
            }
        }
    }

    /**
     * 注册RxBus事件总线，添加事件监听
     */
    private void registerRxBus() {
        compositeDisposable.add(RxBus.get().toObservable(ConnectActionEvent.class).subscribe(new Consumer<ConnectActionEvent>() {
            @Override
            public void accept(ConnectActionEvent connectActionEvent) {
                connectAction(connectActionEvent);
            }
        }));
        compositeDisposable.add(RxBus.get().toObservable(MessageEvent.class).subscribe(new Consumer<MessageEvent>() {
            @Override
            public void accept(MessageEvent messageEvent) {
                messageAction(messageEvent);
            }
        }));
        compositeDisposable.add(RxBus.get().toObservable(ConnectStatusEvent.class).subscribe(new Consumer<ConnectStatusEvent>() {
            @Override
            public void accept(ConnectStatusEvent connectStatusEvent) {
                connectStatusAction(connectStatusEvent);
            }
        }));
        registerRxBus = true;
    }

    /**
     * 连接动作
     *
     * @param connectActionEvent 连接事件
     */
    private void connectAction(ConnectActionEvent connectActionEvent) {
        ActionListener actionListener = removeListener(connectActionEvent.getToken());

        if (null != actionListener) {
            if (connectActionEvent.getStatus() == Constants.ACTION_SUCCESS) {
                actionListener.onSuccess();
            } else {
                actionListener.onFailure(connectActionEvent.getErrorMsg());
            }
        }
    }

    /**
     * 消息动作
     *
     * @param messageEvent 消息事件
     */
    private void messageAction(MessageEvent messageEvent) {
        Log.e("ActionSocke_",messageEvent.getText()+"");
        if (null != messageEvent.getText()) {

            Gson gson = new Gson();
            SocketModel socketModel = null;
            try {
                BaseModel baseModel = gson.fromJson(messageEvent.getText(), BaseModel.class);
                if (null != baseModel) {
                    socketModel = baseModel.getData();
                    LogUtil.d("Keyvalue",socketModel.getKey()+"====value:  "+socketModel.getValue());
                }
            } catch (Exception e) {
                LogUtil.d(TAG, e.getMessage());
            }
            for (EventsListener eventsListener : eventsListeners) {
                eventsListener.message(messageEvent.getText(), socketModel);
            }
        }
    }

    /**
     * 连接状态动作
     *
     * @param connectStatusEvent 连接状态事件
     */
    private void connectStatusAction(ConnectStatusEvent connectStatusEvent) {
        int status = connectStatusEvent.getStatus();
        for (EventsListener eventsListener : eventsListeners) {
            switch (status) {
                case Constants.STATUS_CONNECTED:
                    eventsListener.connected();
                    break;
                case Constants.STATUS_CONNECTING:
                    eventsListener.connecting();
                    break;
                case Constants.STATUS_DISCONNECTED:
                    eventsListener.disconnected(connectStatusEvent.isReconnect());
                    break;
            }
        }
    }

    /**
     * 实际连接处理
     */
    private void doConnect() {

        ActionListener actionListener = getListener(connectToken);

        if (serverUrl == null) {
            if (null != actionListener) {
                actionListener.onFailure(ErrorInfo.ERR_URL_NULL);
            }
            removeListener(connectToken);
            return;
        }

        if (null == socketConnection) {
            socketConnection = webSocketService.getConnection();
        }

        try {
            webSocketService.connect(serverUrl, connectToken);
        } catch (Exception e) {
            if (null != actionListener) {
                actionListener.onFailure(e.getMessage());
            }
            removeListener(connectToken);
        }
    }

    /**
     * 保存回调
     *
     * @param actionListener 动作监听回调
     * @return 异步标签
     */
    private synchronized String storeListener(ActionListener actionListener) {
        tokenMap.put(tokenNumber, actionListener);
        return Integer.toString(tokenNumber++);
    }

    /**
     * 获取回调
     *
     * @param token 异步标签
     * @return 动作监听回调
     */
    private synchronized ActionListener getListener(String token) {
        return tokenMap.get(Integer.parseInt(token));
    }

    /**
     * 移除回调
     *
     * @param token 异步标签
     * @return 动作监听回调
     */
    private synchronized ActionListener removeListener(String token) {
        if (null != token) {
            int tokenNumber = Integer.parseInt(token);
            ActionListener listener = tokenMap.get(tokenNumber);
            tokenMap.delete(tokenNumber);
            return listener;
        }
        return null;
    }

    /**
     * 获取服务器基址
     *
     * @return 服务器基址
     */
    private String getBaseUrl() {
        if (!TextUtils.isEmpty(baseUrl)) {
            return baseUrl;
        }

        if (BuildConfig.DEBUG) {
            return Constants.SOCKET_URL_DEBUG;
        } else {
            return Constants.SOCKET_URL_RELEASE;
        }
    }
}
